package net.sxnic.ugr.security;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sxinfo.core.entity.EntityAlreadyExistsException;
import net.sxnic.ugr.context.UserContextHolder;
import net.sxnic.ugr.group.Group;
import net.sxnic.ugr.group.GroupManager;
import net.sxnic.ugr.group.dao.GroupDao;
import net.sxnic.ugr.menu.Menu;
import net.sxnic.ugr.menu.dao.MenuDao;
import net.sxnic.ugr.menu.util.MenuComparator;
import net.sxnic.ugr.permission.Permission;
import net.sxnic.ugr.permission.dao.PermissionDao;
import net.sxnic.ugr.resource.Resource;
import net.sxnic.ugr.resource.dao.ResourceDao;
import net.sxnic.ugr.role.Role;
import net.sxnic.ugr.role.dao.RoleDao;
import net.sxnic.ugr.user.User;
import net.sxnic.ugr.user.UserManager;
import net.sxnic.ugr.user.dao.UserDao;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("authorizationManager")
public class DefaultAuthorizationManager implements AuthorizationManager {

	private static final Logger logger = LoggerFactory
			.getLogger(DefaultAuthorizationManager.class);

	@Autowired
	private UserManager userManager;

	@Autowired
	private GroupManager groupManager;

	@Autowired
	private RoleDao roleDao;
	
	@Autowired
	private GroupDao groupDao;
	
	@Autowired
	private UserDao userDao;

	@Autowired
	private PermissionDao permissionDao;

	@Autowired
	private ResourceDao resourceDao;

	@Autowired
	private MenuDao menuDao;

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#grantRole(net.sxinfo.application.user.User,
	 *      java.lang.String)
	 */
	public void grantRole(User user, String role) {
		Role r = getRole(role);
		if (r == null) {
			throw new IllegalStateException("Null role object");
		}
		user.addRole(r);
		userManager.update(user);
	}

	public void grantRole(User user, Role role) {
		user.addRole(role);
		userManager.update(user);
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#revokeRole(net.sxinfo.application.user.User,
	 *      java.lang.String)
	 */
	public void revokeRole(User user, String role) {
		user.removeRole(getRole(role));
		userManager.update(user);
	}

	public void revokeRole(User user, Role role) {
		user.removeRole(role);
		userManager.update(user);
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#grantRole(net.sxinfo.application.group.Group,
	 *      java.lang.String)
	 */
	public void grantRole(Group group, String role) {
		Role r = getRole(role);
		if (r == null) {
			throw new IllegalStateException("Null role object");
		}
		group.addRole(r);
		groupManager.update(group);
	}

	public void grantRole(Group group, Role role) {
		group.addRole(role);
		groupManager.update(group);
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#revokeRole(net.sxinfo.application.group.Group,
	 *      java.lang.String)
	 */
	public void revokeRole(Group group, String role) {
		group.removeRole(getRole(role));
		groupManager.update(group);
	}

	public void revokeRole(Group group, Role role) {
		group.removeRole(role);
		groupManager.update(group);
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getRole(java.lang.String)
	 */
	public Role getRole(String role) {
		return roleDao.findByUnique("role",role);
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getUser(java.lang.String)
	 */
	public  User getUser(String username) {
		return this.userDao.findByUnique("username", username);
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getUser(java.lang.String)
	 */
	public  Group getGroup(String group) {
		return this.groupDao.findByUnique("group", group);
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getRole(java.lang.String)
	 */
	public Permission getPermission(String name) {
		return this.permissionDao.findByUnique("code",name);
	}
 
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getRoleById(java.lang.String)
	 */
	public Role getRoleById(String id) {
		return roleDao.get(id);
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getRoleById(java.lang.String)
	 */
	public Menu getMenu(String code) {
		return  menuDao.findByUnique("code",code);
	}
	
 
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getPermissionById(java.lang.String)
	 */
	public Permission getPermissionById(String id) {
		return permissionDao.get(id);
	}
 

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getRoles()
	 */
	public List<Role> getRoles() {
		return roleDao.getAll();
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getGroups()
	 */
	public  List<Group> getGroups() {
		return groupDao.getAll();
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getMenus()
	 */
	public  List<Menu> getMenus() {
		return this.menuDao.getAllByOrder();
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getUsers()
	 */
	public  List<User> getUsers() {
		return userDao.getAll();
	}
	
	  
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getPermissions()
	 */
	public  List<Permission> getPermissions() {
		return this.permissionDao.getAll();
	}
	
	 
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getUsersInRole(net.sxinfo.application.role.Role)
	 */
	public List<User> getUsersInRole(Role role) {
		return userDao.getUserInRole(role);
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#getGroupsInRole(net.sxinfo.application.role.Role)
	 */
	public List<Group> getGroupsInRole(Role role) {
		return groupDao.getGroupsInRole(role);
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#createRole(net.sxinfo.application.role.Role)
	 */
	public void createRole(Role role) throws EntityAlreadyExistsException {
		if (getRole(role.getRole()) != null) {
			throw new EntityAlreadyExistsException();
		}
		roleDao.save(role);
	}
	
 

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#deleteRole(net.sxinfo.application.role.Role)
	 */
	public void deleteRole(Role role) {
		roleDao.delete(role);
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#updateRole(net.sxinfo.application.role.Role)
	 */
	public void updateRole(Role role) {
		roleDao.update(role);
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#updateRole(net.sxinfo.application.role.Role)
	 */
	public void updateGroup(Group group)  {
		this.groupDao.update(group) ;
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#deleteRole(net.sxinfo.application.role.Role)
	 */
	public void deleteGroup(Group group) {
		this.groupDao.delete(group);
	}

	
	
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#createPermission(net.sxinfo.application.role.Role)
	 */
	public void createPermission(Permission permission) throws EntityAlreadyExistsException {
		if (this.getPermission(permission.getUrl()) != null) {
			throw new EntityAlreadyExistsException();
		}
		permissionDao.save(permission);
	}
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#deleteRole(net.sxinfo.application.role.Role)
	 */
	public void deletePermission(Permission permission) {
	    
		permissionDao.delete(permission);
	}
	 
	
	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#updateRole(net.sxinfo.application.role.Role)
	 */
	public void updatePermission(Permission permission) {
		this.permissionDao.update(permission);
	}
	 
 

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#isUserInRole(net.sxinfo.application.user.User,
	 *      java.lang.String)
	 */
	public boolean isUserInRole(User user, String role) {
		if (user == null || !user.getEnabled()) {
			return false;
		}

		/*
		 * if (user.getRoles().contains(new Role("admin"))) { return true; }
		 */

		if (user.getRoles().contains(getRole(role))) {
			return true;
		}

		return false;
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#isGroupInRole(net.sxinfo.application.group.Group,
	 *      java.lang.String)
	 */
	public boolean isGroupInRole(Group group, String role) {
		if (group == null || !group.isEnabled()) {
			return false;
		}

		/*
		 * if (group.getRoles().contains(new Role("admin"))) { return true; }
		 */

		if (group.getRoles().contains(getRole(role))) {
			return true;
		}

		return false;
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#isUserGroupInRole(net.sxinfo.application.user.User,
	 *      java.lang.String)
	 */
	public boolean isUserGroupInRole(User user, String role) {
		if (user == null) {
			return false;
		}

		if (isUserInRole(user, role)) {
			return true;
		}

		for (Group group : user.getGroups()) {
			if (isGroupInRole(group, role)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @see net.sxinfo.application.security.AuthorizationManager#isInRole(java.lang.String,
	 *      java.lang.String[])
	 */
	public boolean isInRole(String rule, String... roles) {
		if (!IF_ANY_GRANTED.equals(rule) && !IF_ALL_GRANTED.equals(rule)
				&& !IF_NOT_GRANTED.equals(rule)) {

			throw new IllegalArgumentException(
					"Rule must be ifAnyGranted, ifAllGranted or ifNotGranted");
		}

		User user = UserContextHolder.getUser();

		if (user == null) {
			logger.debug("null user object");
			return false;
		}

		// 如果用户具有最高admin的角色
		// 那么他就相当于具有了全部角色
		if (!IF_NOT_GRANTED.equals(rule)
				&& isUserGroupInRole(user, Role.SYSTEM_ADMIN)) {
			logger.debug("He is a System Admin");
			return true;
		}

		// 开始进行正式的角色判断
		for (String role : roles) {
			if (isUserGroupInRole(user, role)) {
				if (IF_ANY_GRANTED.equals(rule)) {
					return true;
				} else if (IF_NOT_GRANTED.equals(rule)) {
					return false;
				}
			} else {
				if (IF_ALL_GRANTED.equals(rule)) {
					return false;
				}
			}
		}

		if (IF_ANY_GRANTED.equals(rule)) {
			return false;
		} else if (IF_NOT_GRANTED.equals(rule)) {
			return true;
		} else if (IF_ALL_GRANTED.equals(rule)) {
			return true;
		} else {
			return false;
		}
	}

	
	

	public void addRolePermission(String role, String... permissionNames) {
		if (StringUtils.isBlank(role) || permissionNames == null
				|| permissionNames.length == 0) {
			return;
		}

		Role r = null;
		r = roleDao.findByUnique("role", role);

		Permission p = null;
		for (String res : permissionNames) {

			p = permissionDao.findByUnique("name", res);

			

			if (p == null) {
				logger.error("==addRolePermission===error===r:="+r.getRole()+"===p:=="+res);
				continue;
			}

			if (r.getPermissions().contains(p)) {
				continue;
			}

			r.addPermission(p);

			roleDao.save(r);
		}
	}

	public void addRolePermission(Role role, Permission permission) {
		if (role == null || permission == null)
			return;

		if (role.getPermissions().contains(permission)) {
			return;
		}

		role.addPermission(permission);
		roleDao.save(role);

	}

	@SuppressWarnings("unchecked")
	public Set<Menu> findMenus(String userName) {
		Set<Role> rSet = findUsersRole(userName);

		if (rSet == null || rSet.size() <= 0) {
			return null;
		}

		List<Menu> setList = new ArrayList<Menu>();

		for (Role r : rSet) {
			for (Menu m : r.getMenus()) {
				if (!setList.contains(m)) {
					setList.add(m);
				}
			}
		}

		// 排序处理
		MenuComparator comparator = new MenuComparator();

		Collections.sort(setList, comparator);

		Set<Menu> mSet = new LinkedHashSet<Menu>();
		for (Menu m : setList) {

			mSet.add(m);
		}
		return mSet;		 
	}
	
	/**
	 * 获取用户菜单
	 * 
	 * @return
	 */
	public   Map<String, String> findUserMenu(String userName) {
 
		Map<String, String> map = new HashMap<String, String>();
		Set<Menu> menus =  findMenus (userName);
		 
		if (menus.size() > 0) {
			for (Menu menu : menus) {
				map.put(menu.getValue(), menu.getValue());
			}
		} else {
			return null;
		}

		return map;
	}
	
	public List<Menu> getSubLevel(String parentMenuCode) {
		return menuDao.getSubLevel(parentMenuCode);
	}

	public List<Menu> getTopLevel() {
		return menuDao.getTopLevel();
	}

	public Set<Permission> findUsersPermission(String userName) {
		User u = userManager.findByUnique("username", userName);
		
		if (u == null)
			return null;

		Set<Permission> set = new HashSet<Permission>();

		Set<Role> roles = u.getRoles();

		for (Group g : u.getGroups()) {
			for (Role r : g.getRoles()) {
				if (!roles.contains(r)) {
					roles.add(r);
				}
			}
		}

		for (Role r : roles) {
			for (Permission p : r.getPermissions()) {
				if (!set.contains(p)) {
					set.add(p);
				}
			}
		}

		return set;
	}
	
	public Set<Role> findUsersRole(String userName) {
		User u = userManager.findByUnique("username", userName);
		
		if (u == null)
			return null;

		 

		Set<Role> roles = u.getRoles();

		for (Group g : u.getGroups()) {
			for (Role r : g.getRoles()) {
				if (!roles.contains(r)) {
					roles.add(r);
				}
			}
		}
		return roles;
	}


	public boolean isUserCanAccessResource(String userName, String res) {
		if (StringUtils.isBlank(userName) || StringUtils.isBlank(res)) {
			return false;
		}

		User u = userManager.getUserByUsername(userName);

		if (u == null) {
			return false;
		}

		// 解析资源名称 如: /comm/basecode_save
		String entityName_1 = StringUtils.substringAfterLast(res, "/");
		String entityName = StringUtils.substringBefore(entityName_1, "_");

		List<Permission> pList = permissionDao.findBy("name",
				entityName.toLowerCase() + Permission.SYSTEM_ADMIN);

		if (pList == null || pList.size() <= 0) {
			// 如果有entityAdmin的权限，则视为可以访问
			// 如果没有entityAdmin的权限，则需要在具体的Resource表中查询
			List<Resource> rList = resourceDao.findBy("name", entityName_1);

			if (rList == null || rList.size() <= 0) {
				return false;
			}

			// 找到此用户所拥有的所有权限，并遍历rList看是否有资源所有对应的权限，在此用户所拥有的全部权限之内
			Set<Permission> pSet = findUsersPermission(userName);

			for (Resource r : rList) {
				if (pSet.contains(r)) {
					return true;
				}
			}
		} else {
			return true;
		}

		return false;
	}

	public void removeRolePermission(String role, String... permissionNames) {
		if (StringUtils.isBlank(role) || permissionNames == null
				|| permissionNames.length == 0) {
			return;
		}

		Role r = null;
		r = roleDao.findByUnique("name", role);
		if (r == null) {
			return;
		}

		Permission p = null;
		for (String res : permissionNames) {
			p = permissionDao.findByUnique("role", res);
			if (r == null) {
				continue;
			}

			if (!r.getPermissions().contains(p)) {
				continue;
			}

			r.removePermission(p);
			roleDao.save(r);
		}

	}


	@Override
	public boolean isUserInGroup(User user, String groupName) {

		Group group = groupManager.findByUnique("name", groupName);

		if (group == null || !group.isEnabled()) {
			return false;
		}

		if (user.getGroups().contains(group)) {
			return true;
		}

		return false;
	}
	
	@Override
	public boolean isUserInGroup(String username, String groupName) {

		Group group = groupManager.findByUnique("name", groupName);
		User user=userManager.getUserByUsername(username);

		if (group == null || !group.isEnabled()) {
			return false;
		}

		if (user.getGroups().contains(group)) {
			return true;
		}

		return false;
	}

	@Override
	public List<User> getUsersInGroup(Group group) {		
		return userDao.getUserInGroup(group);
	}
	
	public void addRoleMenu(String role, String... menus) {
	 
		if (StringUtils.isBlank(role) || menus == null
				|| menus.length == 0) {
			return;
		}
		 
		Role r = null;
		r = roleDao.findByUnique("role", role);
		if (r == null) {
			return;
		}
		

		Menu m = null;
		for (String res : menus) {
			m = menuDao.findByUnique("code", res);
			
			if (m == null) {
				continue;
			}

			if (r.getMenus().contains(m)) {
				continue;
			}
	
			r.addMenu(m);
			
			roleDao.save(r);
		}

	}

	@Override
	public List<User> getUsersInRole(String roleCode) {		
		return getUsersInRole(getRole(roleCode));
	}

	@Override
	public List<User> getUsersInGroup(String groupCode) {		
		return getUsersInGroup(getGroup(groupCode));
	}

	@Override
	public List<Group> getGroupsInRole(String roleCode) {		
		return getGroupsInRole(getRole(roleCode));
	}
	
	 

}
